﻿using System;
using System.Collections.Generic;
using System.Drawing;
using EvoBoost.Core;

namespace EvoBoost.ComputerVision.ViolaJones
{
	[Serializable]
	public class HaarFeatureSplit : SplitBase<IntegralImage>
	{
		private static readonly int haarFeatureTypeCount = Enum.GetValues(typeof (HaarFeatureType)).Length;
		
		private HaarFeatureSplit(Size imageSize, IEnumerable<ISplitParameterDescription> parameterDescriptions)
			: base(parameterDescriptions)
		{
			this.ImageSize = imageSize;
		}

		public Size ImageSize { get; private set; }

		public static HaarFeatureSplit Create(Size imageSize, bool enableFeatureCache)
		{
			List<ISplitParameterDescription> parameters = new List<ISplitParameterDescription>();
			parameters.Add(new MinMaxTruncSplitParameterDescription(0, imageSize.Width, imageSize.Width, imageSize.Width / 6));		// Left
			parameters.Add(new MinMaxTruncSplitParameterDescription(0, imageSize.Height, imageSize.Height, imageSize.Height / 6));	// Top
			parameters.Add(new MinMaxTruncSplitParameterDescription(0, imageSize.Width, imageSize.Width, imageSize.Width / 6));		// Right
			parameters.Add(new MinMaxTruncSplitParameterDescription(0, imageSize.Height, imageSize.Height, imageSize.Height / 6));	// Bottom
			parameters.Add(new RandomSplitParameterDescription(Enum.GetValues(typeof(HaarFeatureType)).Length));					// Feature

			return new HaarFeatureSplit(imageSize, parameters);
		}

		public override double Calculate(IntegralImage sampleProperties, IList<double> parameterValues)
		{
			if (sampleProperties == null) throw new ArgumentNullException("sampleProperties");
			if (parameterValues == null) throw new ArgumentNullException("parameterValues");
			if (parameterValues.Count != 5) throw new ArgumentNullException("parameterValues", "Values of 5 parameters should be provided.");
			if (sampleProperties.Size != this.ImageSize) throw new ArgumentException("Image of invalid size given.", "sampleProperties");

			// Extract rectangle
			int left = (int) parameterValues[0];
			int top = (int) parameterValues[1];
			int right = (int) parameterValues[2];
			int bottom = (int) parameterValues[3];
			
			// Extract feature type
			int featureTypeAsInt = (int) parameterValues[4];
			featureTypeAsInt %= haarFeatureTypeCount;
			HaarFeatureType featureType = (HaarFeatureType) featureTypeAsInt;

			// Swap rectangle corners if necessary
			int temp;
			if (left > right)
			{
				temp = left;
				left = right;
				right = temp;
			}
			if (top > bottom)
			{
				temp = top;
				top = bottom;
				bottom = temp;
			}

			// Build feature rectangle
			Rectangle rectangle = new Rectangle(left, top, right - left, bottom - top);
			if (!new Rectangle(Point.Empty, this.ImageSize).Contains(rectangle))
				throw new ArgumentException("One or more parameters describing feature rectangle (0-3) are invalid.", "parameterValues");

			// Calculate feature value
			double featureValue = HaarFeature.CalculateNoCheck(sampleProperties, rectangle, featureType);
			// It probably decreases training time too much with no profit
			featureValue /= (sampleProperties.BrightnessDeviation > 0 ? sampleProperties.BrightnessDeviation : 1);
			return featureValue;
		}
	}
}